/*
 * ============================================================================
 *
 *       Filename:  thread_key_example.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2014年03月18日 13时50分59秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jianxi sun (jianxi), ycsunjane@gmail.com
 *   Organization:  
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <pthread.h>

#define KEY_NUM 100

pthread_once_t pthread_key_flag = PTHREAD_ONCE_INIT;

struct key_ref_t {
	pthread_key_t *keyp;
	int cnt;
	pthread_mutex_t mutex;
} key_ref;

void ref_get(pthread_mutex_t *mutex, int *cnt)
{
	pthread_mutex_lock(mutex);
	(*cnt)++;
	pthread_mutex_unlock(mutex);
}

void ref_put(pthread_mutex_t *mutex, int *cnt, void (*func)(void *), void *arg)
{
	pthread_mutex_lock(mutex);
	(*cnt)--;
	if(*cnt == 0 && func != NULL)
		func(arg);
	pthread_mutex_unlock(mutex);
}

void free_keyp(void *arg)
{
	if(key_ref.keyp) {
		printf("free:%p\n", key_ref.keyp);
		free(key_ref.keyp);
		key_ref.keyp = NULL;
	}
}

void _free_keyp(void *ptr)
{
	static int index = 0;
	//printf("free:%d,str:%s\n", index++, ptr);
	if(ptr)
		free(ptr);
	ptr = NULL;
	ref_put(&key_ref.mutex, &key_ref.cnt, free_keyp, NULL);
}

void _pthread_key_create()
{
	key_ref.keyp = malloc(sizeof(pthread_key_t) * KEY_NUM);
	if(key_ref.keyp == NULL) {
		printf("init key failed\n");
		exit(-1);
	}

	int ret;
	ret = pthread_key_create(key_ref.keyp, _free_keyp);
	switch(ret) {
	case EAGAIN:
		printf("Have no enough resources, max PTHREAD_KEYS_MAX:%d\n", 
			PTHREAD_KEYS_MAX);
		exit(-1);
	case ENOMEM:
		printf("Have no enough memory\n");
		exit(-1);
	default:
		break;
	}
	return;
}

void pthread_key_malloc(long long threadid)
{
	pthread_once(&pthread_key_flag, _pthread_key_create);

	ref_get(&key_ref.mutex, &key_ref.cnt);
	char * user = malloc(100);
	if(user)
		sprintf(user, "%s,%llu\n","xiaojuan", threadid);
	else
		exit(-1);
	pthread_setspecific(*key_ref.keyp, user);
	printf("user:%p\n", pthread_getspecific(*key_ref.keyp));
}

void *key_example(void *arg)
{
	long long threadid = *((long long *)arg);
	printf("thread id:%lld \n", threadid);
	pthread_key_malloc(threadid);

	char *ret_str = malloc(20);
	if(ret_str)
		strcpy(ret_str, "jianxi");
	pthread_exit(ret_str);
}

int main()
{
	pthread_t tid, tid2;
	void *arg = &tid;
	void *arg2 = &tid2;

	key_ref.cnt = 1;
	pthread_mutex_init(&key_ref.mutex, NULL);

	int ret;
	ret = pthread_create(&tid, NULL, key_example,  arg);
	if(ret) {
		perror(strerror(errno));
		return -1;
	}

	ret = pthread_create(&tid2, NULL, key_example,  arg2);
	if(ret) {
		perror(strerror(errno));
		return -1;
	}

	char *thread_str;
	pthread_join(tid, (void **)&thread_str);
	printf("Exit:%s\n", thread_str);
	free(thread_str);

	pthread_join(tid2, (void **)&thread_str);
	printf("Exit:%s\n", thread_str);
	free(thread_str);
	return 0;
}
